package cn.jhz.learn.community_dynamic.security.filter;

import cn.jhz.learn.community_dynamic.security.AuthenticationParameterException;
import cn.jhz.learn.community_dynamic.security.model.PhoneSmsCodeAuthenticationToken;
import cn.jhz.learn.community_dynamic.security.model.Login;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.validation.*;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.Collectors;


@Component
public class JsonPhoneSmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private final SmartValidator smartValidator;
    private final ObjectMapper mapper;

    @Autowired
    public JsonPhoneSmsCodeAuthenticationFilter(@Lazy AuthenticationManager authenticationManager,@Qualifier("jsonLoginSuccessHandler") AuthenticationSuccessHandler authenticationSuccessHandler,
            AuthenticationFailureHandler authenticationFailureHandler,
            SmartValidator smartValidator, 
            ObjectMapper mapper, 
            @Qualifier("phoneLoginAntPathRequestMatcher") AntPathRequestMatcher phoneLoginAntPathRequestMatcher) {
	//拦截url为 "/api/*/user/phone_login" 的POST请求
	super(phoneLoginAntPathRequestMatcher);
	super.setAuthenticationManager(authenticationManager);
	this.setAuthenticationSuccessHandler(authenticationSuccessHandler);
	this.setAuthenticationFailureHandler(authenticationFailureHandler);
        this.smartValidator = smartValidator;
        this.mapper = mapper;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //当Content-Type为json时尝试身份验证
        if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            //使用jackson反序列化json
            PhoneSmsCodeAuthenticationToken authRequest = null;
            try (InputStream is = request.getInputStream()){

                Login authenticationBean = mapper.readValue(is, Login.class);
                authRequest = this.createToken(authenticationBean);

            }catch (IOException e) {
                e.printStackTrace();
                //TODO 包装IO异常
                authRequest = new PhoneSmsCodeAuthenticationToken("", "");
            }
            return this.getAuthenticationManager().authenticate(authRequest);
        }
        else {
            throw new AuthenticationServiceException("Authentication type not supported: " + request.getContentType());
        }
    }

    private PhoneSmsCodeAuthenticationToken createToken(Login login){
        Errors errors = new DirectFieldBindingResult(login, "login");
        ValidationUtils.invokeValidator(this.smartValidator, login, errors, Login.PhoneLogin.class);
        if (errors.hasErrors()) {
            throw new AuthenticationParameterException(
                    errors.getAllErrors().stream()
                            .map(ObjectError::getDefaultMessage)
                            .collect(Collectors.joining(",","","!"))
            );
        }else {
            return new PhoneSmsCodeAuthenticationToken(login.getPhone(), login.getCode());
        }
    }
    
//    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }
}
